// Local filesystem implementation
// Copyright 2005 (C) Ralph Thomas

#include <model/file/fileModel.h>
#include <model/attributeNames.h>
#include <model/actionNames.h>
#include <model/attributeInfo.h>
#include <model/error.h>
#include <model/collection.h>
#include <util/thread.h>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>
#include <adobe/config.hpp>
#include <adobe/adam.hpp>
#include <adobe/adam_evaluate.hpp>
#include <adobe/adam_parser.hpp>
#include <util/readFile.h>
#include <util/resources.h>
#include <toolkit/application.h>
#include <iostream>
#include <boost/filesystem/fstream.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <sstream>
#include <deque>
#ifdef	ADOBE_PLATFORM_MAC
// XXX: Implement BSD xattr.
#include <model/file/typerMac.h>
#elif	ADOBE_PLATFORM_LINUX
#include <xdgmime.h>
#include <model/file/linuxAttr.h>
#elif	ADOBE_PLATFORM_WIN
#include <model/file/typerWin32.h>
#endif
#include <jpeglib.h>
#include <jpeg/transupp.h>
extern "C" {
#include <jpegtools.h>
#include <genthumbnail.h>
}
#include <string.h>

#define THUMB_MAX	65536

namespace bfs = boost::filesystem;

namespace model {
	//
	/// This thread creates the thumbnail images from full-size images.
	/// It is refcounted, and only one ever exists.
	//
	class thumbnailThread : public util::thread {
		int _refCount;
		static thumbnailThread* _instance;
		thumbnailThread() : _refCount( 1 ) {}
		virtual ~thumbnailThread() {}
	  public:
		//
		/// Return the single instance of the thumbnail thread.
		///
		/// \return	single instance of thumbnail thread.
		//
		static thumbnailThread* get() {
			if( !_instance ) {
				_instance = new thumbnailThread();
				return _instance;
			}
			//
			// Increase refcount and return.
			//
			_instance->_refCount++;
			return _instance;
		}
		//
		/// Release a reference to the thumbnail thread.
		//
		void release() {
			_instance->_refCount--;
			if( _instance->_refCount == 0 ) {
				delete _instance;
				_instance = 0;
			}
		}
		//
		/// Create a thumbnail from the given file path, and call into
		/// the given model when the thumbnail has been created.
		///
		/// \param	path	the path of the image to create a
		///			thumbnail from.
		/// \param	m	the model to inform once the thumbnail
		///			has been created.
		//
		void thumbnail( const std::string& type, const bfs::path& path, const fileModel* m ) {
			class thumbnailWorker : public util::thread::work {
				fileModel* 		_model;
				bfs::path 		_path;
				std::string		_type;
				//
				// This runs in the main thread and that means we don't need mutexes
				// in the file model (which slow things down a little).
				//
				static void reportIcon( fileModel* model, adobe::value_t icon ) {
					model->iconComplete( icon );
					model->release();
				}
			  public:
				thumbnailWorker( fileModel* m,
					const std::string& t,
					const bfs::path& p )
					: _model( m ), _path( p ), _type( t ) {}
				virtual ~thumbnailWorker() {}
				virtual priority getPriority() const {
					return kWorkPriorityRegular;
				}
				virtual void doWork() {
					if( !_model ) return;
					//
					// Create the icon and give it
					// to the model.
					//
					adobe::value_t icon = toolkit::checkPreview( _type, _path );
					if( icon == adobe::value_t() ) icon = toolkit::createPreview( _type, _path );
					toolkit::application::getInstance().executeInMain(
						boost::bind( &thumbnailWorker::reportIcon, _model, icon ) );
				}
			};
			//
			// Copy the model, create some work and submit it to
			// the thread.
			//
			if( !m ) return;
			fileModel* target(
				static_cast<fileModel*>( m->retain() ) );
			thumbnailWorker* work(
				new thumbnailWorker( target, type, path ) );
			adoptWork( work );
		}
	};
	class thumbLoadQueue {
		struct thumbLoadRequest {
			fileModel*	_model;
			bfs::path	_path;
			std::string	_type;

			thumbLoadRequest( const thumbLoadRequest& rhs ) : _path( rhs._path ), _type( rhs._type ) {
				_model = static_cast<fileModel*>( rhs._model->retain() );
			}
			thumbLoadRequest( const fileModel* m, const bfs::path& p, const std::string& t ) : _path( p ), _type( t ) {
				_model = static_cast<fileModel*>( m->retain() );
			}
			~thumbLoadRequest() { _model->release(); }
			
			void load() {
				adobe::value_t icon = toolkit::checkPreview( _type, _path );
				if( icon == adobe::value_t() ) {
					thumbnailThread* t( thumbnailThread::get() );
					if( t ) t->thumbnail( _type, _path, _model );
					t->release();
					return;
				}
				_model->iconComplete( icon );
			}
		};

		typedef std::deque<thumbLoadRequest> loadQueue;
		loadQueue	_queue;
		adobe::value_t	_default;
		bool		_scheduled;
		thumbLoadQueue() : _scheduled( false ) {
			//
			// XXX: This hardcoded fallback icon is wrong!
			//
			_default = toolkit::getIcon( "image/jpeg", bfs::path() );
		}
		void onIdle() {
			_scheduled = false;
			if( _queue.empty() ) return;
			thumbLoadRequest r( _queue.front() ); _queue.pop_front();
			r.load();
			if( !_queue.empty() ) {
				_scheduled = true;
				toolkit::application::getInstance().executeInMain( boost::bind( &thumbLoadQueue::onIdle, this ) );
			}
		}
	  public:
		~thumbLoadQueue() {}
		adobe::value_t load( const std::string& t, const bfs::path& p, const fileModel* m ) {
			_queue.push_back( thumbLoadRequest( m, p, t ) );
			if( !_scheduled ) {
				toolkit::application::getInstance().executeInMain( boost::bind( &thumbLoadQueue::onIdle, this ) );
				_scheduled = true;
			}
			return _default;
		}
		static boost::shared_ptr<thumbLoadQueue> get() {
			static boost::weak_ptr<thumbLoadQueue> weak_instance;
			boost::shared_ptr<thumbLoadQueue> instance( weak_instance.lock() );
			if( instance ) return instance;
			instance = boost::shared_ptr<thumbLoadQueue>( new thumbLoadQueue );
			weak_instance = instance;
			return instance;
		}
	};
	thumbnailThread* thumbnailThread::_instance = 0;
	fileModel::fileMap	fileModel::_fileMap;

	fileModel* fileModel::fromString( const std::string& serialized ) {
//		lock lk( _fileMutex );
		//
		// We have to do most of the job of the factory here, which is:
		//
		//  1. Verify various things about the URL.
		//  2. Normalize the URL.
		//  3. Lookup that URL in our model list, or
		//  4. Create a new model.
		//
		//
		// Ensure that this is indeed a file URL. We don't use the
		// generic mission URL class here because file URLs abide
		// by different rules and are far simpler than regular URLs.
		//
		std::string url( serialized );
		std::string file( "file://" );
		std::string protocol( url.substr( 0, 7 ) );
		if( protocol != file ) return 0;
		url = url.substr( 7 );
		//
		// Normalize our URL. For file URLs this means that we get
		// boost::filesystem::path to give us the canonical form
		// of the path.
		//
		bfs::path p;
		try {
			p = bfs::path( url, bfs::native );
			url = file + p.string();
		} catch( ... ) {
			return 0;
		}
		//
		// If this path doesn't exist then don't return a model
		// for it. This policy might have to be revised when we
		// need to create files (but perhaps that's something
		// that the parent should do, rather than making a model
		// blind).
		//
		if( !bfs::exists( p ) ) return 0;
		//
		// Check that we don't already have a model for this URL.
		//
		fileMap::iterator i = _fileMap.find( url );
		if( i == _fileMap.end() ) {
			//
			// The model doesn't exist yet, create it and return
			// it.
			//
			fileModel* fm = new fileModel( p,
				thumbnailThread::get() );
			if( !fm->init() ) {
				//
				// Couldn't initialize the file model, delete
				// it.
				//
				delete fm;
				return 0;
			}
			//
			// Store this file model so that we can find it next
			// time.
			//
			_fileMap[url] = fm;
			return fm;
		}
		//
		// We have found the file model already created. Return it.
		//
		return static_cast<fileModel*>( i->second->retain() );
	}
	fileModel::fileModel( const bfs::path& path, thumbnailThread* thth ) :
		_refCount( 1 ), _path( path ), _error( 0 ),
		_scheduled( false ), _thread( thth ), _loadQueue( thumbLoadQueue::get() ) {
		_theUrl = "file://" + _path.string();
	}
	fileModel::~fileModel() {
		if( _thread ) _thread->release();
	}
	void fileModel::_childNotify( const std::string& child, const std::string& attr,
		const adobe::value_t& value ) {
		//
		// If the child's parent changed then it might not be our
		// child anymore. Check that.
		//
		if( attr == kParent ) {
			try {
				std::string newParent( value.get<std::string>() );
				std::string us( getAttribute( kUrl ).get<std::string>() );
				if( us != newParent ) _children.removeChild( child );
			} catch( ... ) {}
		} else _children.childNotify( child, attr );
		_receivers.notify( this, kChildren );
	}
	void fileModel::_weakParentNotify( const std::string& attr,
		const adobe::value_t& value ) {
		//
		// This function checks to see if the parent exists, and if it
		// does, notifies it that this child has changed.
		//
		std::string parent( _getParentUrl() );
		fileMap::iterator i = _fileMap.find( parent );
		if( i == _fileMap.end() ) return;
		//
		// The parent exists, so let's send it a notification message.
		//
		if( i->second ) i->second->_childNotify( _theUrl, attr, value );
	}
	bool fileModel::init() {
		//
		// Initialize this model. We need to do the following things:
		//
		//  1. Verify that our file exists
		//  2. Enumerate our children.
		//  3. Put our children into the sorter.
		//
		//
		// If the path creating failed then we just flag an error and
		// return true rather than returning false. This means that
		// errors get correctly passed around.
		//
		if( !bfs::exists( _path ) ) {
			 _error = kErrorNonExistant;
			return true;
		}
		//
		// Only directories have children (currently)!
		//
		if( !bfs::is_directory( _path ) ) return true;
		//
		// For each child we need to find out the value of the attribute
		// which we're sorting by. We therefore iterate through all of
		// our children, instanciating them (expensive) to get the value
		// which we will sort them by.
		//
		bfs::directory_iterator last;
		bfs::directory_iterator first( _path );
		while( first != last ) {
			std::string child = _buildUrl( *first );
			//
			// Only add this child if it isn't hidden. This
			// ought to be configurable, but I'm not sure
			// how yet.
			//
			std::string leaf( first->leaf() );
			if( leaf.size() && leaf[0] != '.' ) {
				_children.addChild( child );
			}
			first++;
		}
		//
		// Look up what it is that we should sort by, if we have any sort
		// order at all.
		//
		adobe::value_t sortOrderValue = getAttribute( kSortBy );
		std::string sortOrder;
		if( sortOrderValue.get( sortOrder ) ) {
			_children.setSort( sortOrder );
		}
		return true;
	}
	model* fileModel::retain() const {
		fileModel* us = const_cast<fileModel*>( this );
		us->_refCount++;
		return us;
	}
	void fileModel::release() {
		//
		// Remove the cached file data, we have no need to keep it
		// around likely.
		//
		_contents = "";
		_refCount--;
		if( !_refCount ) {
			//
			// Try to find ourselves (and release ourselves) from the
			// map of all file models.
			//
			fileMap::iterator i = _fileMap.find( _theUrl );
			if( i != _fileMap.end() ) _fileMap.erase( i );
			delete this;
		}
	}
	void fileModel::subscribe( receiver* r ) {
		_receivers.subscribe( r );
	}
	void fileModel::unsubscribe( const receiver* r ) {
		_receivers.unsubscribe( r );
	}
	adobe::value_t fileModel::getAttribute( const std::string& attrName ) const {
		if( attrName == kUrl ) {
			return adobe::value_t( _theUrl );
		} else if( attrName == kModelValid ) {
			if( _error == 0 ) return adobe::value_t( true );
			return adobe::value_t( false );
		} else if( attrName == kModelAttributes ) {
			attributeInfo ai;
			ai.addAttribute( kModelValid, false );
			ai.addAttribute( kModelAttributes, false );
			ai.addAttribute( kChildren, false );
			ai.addAttribute( kName, true );
			ai.addAttribute( kType, false );
			ai.addAttribute( kModifiedDate, false );
			ai.addAttribute( kData, true );
			ai.addAttribute( kParent, false ); // true eventually.
			ai.addAttribute( kError, false );
			ai.addAttribute( kSize, false );
			ai.addAttribute( kIcon, false );
			return adobe::value_t( ai );
		} else if( attrName == kChildren ) {
			return adobe::value_t( _children.getChildren() );
		} else if( attrName == kName ) {
			return adobe::value_t( _path.leaf() );
		} else if( attrName == kType ) {
			//
			// We have an existing file. We should check to see if
			// we already cached a MIME type for it.
			//
			if( !_type.empty() ) return adobe::value_t( _type );
			//
			//
			// We detect directories up front, as the file typing
			// code fails on them otherwise.
			//
			if( !bfs::exists( _path ) ) return adobe::value_t(
				std::string( "x-mission/x-invalid-file" ) );
#ifdef	ADOBE_PLATFORM_LINUX
			//
			// If the x_type attribute is set then we can return
			// that.
			//
			std::string extType;
			if( getAttr( _path, "x_type", extType ) == kErrorNone ) {
				const_cast<fileModel*>( this )->_type = extType;
				return adobe::value_t( extType );
			}
#endif
			if( bfs::is_directory( _path ) ) return adobe::value_t(
				std::string( "inode/directory" ) );
			//
			// Now we can actually type the file and return the
			// determined type.
			//
			std::string type = "x-mission/x-file";
#ifdef	ADOBE_PLATFORM_WIN
			type = getFileType( _path );
#elif	ADOBE_PLATFORM_MAC
			type = getFileType( _path );
#elif	ADOBE_PLATFORM_LINUX
			type = xdg_mime_get_mime_type_for_file(
				_path.string().c_str() );
#endif
			//
			// Cache this type in the model.
			//
			const_cast<fileModel*>( this )->_type = type;
			return adobe::value_t( type );
		} else if( attrName == kModifiedDate ) {
			//
			// Last write time is the same as modified date.
			//
			if( !bfs::is_directory( _path ) ) return adobe::value_t(
				bfs::last_write_time( _path ) );
			return adobe::value_t( 0 );
		} else if( attrName == kData ) {
			//
			// If this is a directory, then we can return the list
			// of files. If this is a file then we can return the
			// file contents.
			//
			if( !bfs::exists( _path ) ) return adobe::value_t(
				std::string() );
			if( bfs::is_directory( _path ) ) {
				std::string contents;
				//
				// Go through all of the children, and add them
				// to the list.
				//
				bfs::directory_iterator last;
				bfs::directory_iterator first( _path );
				while( first != last ) {
					contents += first->leaf() + "\t";
					//
					// If it's a directory then we print
					// directory; otherwise we print out
					// the size of the file.
					//
					if( bfs::is_directory( *first ) )
						contents += "Directory";
					else {
						//
						// This number formatter stuff
						// should be moved into i18n.
						//
						std::stringstream size;
						size << bfs::file_size( *first );
						contents += size.str();
					}
					contents += "\n";
					first++;
				}
				return adobe::value_t( contents );
			}
			//
			// Read into a char array, and return that.
			//
			return adobe::value_t( util::readFile( _path ) );
		} else if( attrName == kStreamBuf ) {
			//
			// This doesn't work for directories.
			//
			if( !bfs::is_directory( _path ) ) {
				bfs::filebuf* fbuf = new bfs::filebuf();
				std::streambuf* sbuf = fbuf->open( _path,
					std::ios_base::in | std::ios_base::binary );
				//
				// Note, sbuf == fbuf, so no memory leak.
				// 
				if( sbuf )
					return adobe::value_t( boost::shared_ptr<std::streambuf>( sbuf ) );
			}
		} else if( attrName == kParent ) {
			return adobe::value_t( _getParentUrl() );
		} else if( attrName == kError ) {
			return adobe::value_t( _error );
		} else if( attrName == kSize ) {
			//
			// We can't return a size if we're a directory or file.
			// It might make sense to report the number of children
			// in a directory here.
			//
			if( !bfs::exists( _path ) || bfs::is_directory( _path ) )
				return adobe::value_t( 0 );
			return adobe::value_t( bfs::file_size( _path ) );
		} else if( attrName == kIcon ) {
			//
			// If we have already created our icon then we can
			// return it.
			//
			if( _icon != adobe::value_t() ) return _icon;
			//
			// We need the MIME type to return the icon. If we don't
			// have the MIME type yet then we must fetch it.
			//
			std::string type = 
				getAttribute( kType ).get<std::string>();
			const_cast<fileModel*>( this )->_icon = _loadQueue->load( type, _path, this );
			return _icon;
		} else if( attrName == kView ) {
			//
			// Depending on the type we return a different Eve
			// description. These rules need to be moved out, as
			// they are pretty much the same for all models.
			//
			std::string type =
				getAttribute( kType ).get<std::string>();
			bfs::path res( util::getResource( type, "eve" ) );
			//
			// XXX: ERROR CHECKING IF THE RESOURCE PATH IS EMPTY!
			//
			// Open the file up, and read it in.
			//
			std::string view = util::readFile( res );
			//
			// Return the read-in file. This should be cached.
			//
			return adobe::value_t( view );
		} else if( attrName == kSheet ) {
			//
			// Make sure that we have our type around already.
			//
			std::string type =
				getAttribute( kType ).get<std::string>();
			return adobe::value_t( util::getSheet( type, _theUrl ) );
		} else if( attrName.find( kExtAttrPrefix ) == 0 ) {
			//
			// This is apparently an extended attribute. For now assume that
			// we have an interface for extended attributes.
			//
			std::string val;
#ifdef	ADOBE_PLATFORM_LINUX
			if( getAttr( _path, attrName, val ) != kErrorNone ) {
				//
				// This is a valid attribute name, but it hasn't
				// been set yet, therefore is an empty string.
				//
				return adobe::value_t(std::string());
			}
#else
			//
			// No attribute support on this platform. Return an empty value.
			//
			return adobe::value_t();
#endif
			//
			// Attributes are written as serialized adobe::value_t s; but
			// that doesn't work right now, so you get a string :(.
			//
			return adobe::value_t( val );
		}
		return adobe::value_t();
	}
	void fileModel::setAttribute( const std::string& attrName,
		const adobe::value_t& a ) {
		//
		// We advertise that name and data may be changed.
		//
		if( attrName == kName ) {
			std::string name;
			if( !a.get<std::string>( name ) ) return;
			//
			// We can't rename to an empty string.
			//
			if( name.empty() ) return;
			//
			// Create the new path for this file.
			//
			bfs::path dest = _path.branch_path() /
				bfs::path( name, bfs::native );
			//
			// What do we do if the destination file already exists?
			//
			try {
				bfs::rename( _path, dest );
				_path = dest;
				_receivers.notify( this, kName );
			} catch( ... ) {}
		} else if( attrName == kData ) {
			std::string data;
			if( !a.get<std::string>( data ) ) return;
			//
			// This is a bit scary. We commit the new data to the
			// disk. If the file is a directory then we do nothing
			// (although strictly we should register an error).
			//
			if( bfs::is_directory( _path ) ) return;
			bfs::ofstream out( _path, std::ofstream::out |
				std::ofstream::binary | std::ofstream::trunc );
			//
			// XXX: Issue write errors here, etc.
			//
			if( out.is_open() ) { out << data; out.close(); }
			//
			// Now we need to tell all of the subscribed clients
			// that everything about this file has just changed.
			//
			// Soon, hopefully, a filesystem notification API will
			// be written so that this is unrequired (i.e.: the
			// notification API will notice that the file got
			// written and tell the model to tell the clients to
			// update).
			//
			//_type = "";
			_contents = data;
			//_receivers.notify( this, kType );
			_receivers.notify( this, kData );
			_receivers.notify( this, kSize );
			_receivers.notify( this, kStreamBuf );
			//
			// The data changed. If we are a JPEG image then we
			// should kick off thumbnail generation. When the
			// new thumbnail has been generated, clients will
			// be notified that the icon has changed.
			//
			if( getAttribute( kType ).get<std::string>() == "image/jpeg" ) {
				_scheduled = true;
				_thread->thumbnail( "image/jpeg", _path, this );
			}
		} else if( attrName.find( kExtAttrPrefix ) == 0 ) {
#ifdef	ADOBE_PLATFORM_LINUX
			//
			// Set an extended attribute. Serialization is busted
			// in ASL on this platform (Linux/gcc4.0.1) currently
			// so everything is a string :(.
			//
			try {
				std::string val = a.get<std::string>();
				setAttr( _path, attrName, val );
				//
				// Make sure that we inform anybody else who
				// might be paying attention that something
				// changed.
				//
				_receivers.notify( this, attrName );
			} catch( ... ) {}
#endif
		} else if( attrName == kUrl ) {
			//
			// This notification is generated by ASL.
			//
			return;
		}
		//
		// Additional actions to perform.
		//
		if( attrName == kSortBy ) {
			std::string val;
		       	if( a.get( val ) ) {
				_children.setSort( val );
				_receivers.notify( this, kChildren );
			}
		}
		//
		// Tell our parent that something changed, if our parent is around.
		// 
		_weakParentNotify( attrName, a );
	}
	adobe::value_t fileModel::execute( const std::string& action,
		const adobe::value_t& arg ) {
		if( action == kTryDrop ) {
			//
			// We can only accept drops if we are a directory.
			//
			return adobe::value_t( bfs::is_directory( _path ) );
		} else if( action == kDrop ) {
			std::string url;
			if( !arg.get( url ) ) return adobe::value_t( false );
			//
			// If we're not a URL then dropping won't work anyway.
			//
			if( !bfs::is_directory( _path ) ) return adobe::value_t( false );
			//
			// Attempt to create a file model from the given URL.
			// We only support dropping file models currently.
			//
			fileModel* m( fileModel::fromString( url ) );
			if( !m ) return adobe::value_t( false );
			//
			// We have a model, extract it's path and attempt a
			// copy operation.
			//
			try {
				//
				// Calculate the new name of the file.
				//
				bfs::path newPath( _path /
					bfs::path( m->_path.leaf(), bfs::native ) );
				bfs::rename( m->_path, newPath );
				//
				// Get the parent URL, build a new child URL and
				// notify everybody about the changes.
				//
				m->_weakParentNotify( kParent, getAttribute( kUrl ) );
				std::string childUrl( _buildUrl( newPath ) );
				//
				// Now the child is ours.
				//
				_children.addChild( childUrl );
				//
				// Inform our viewers that we have a new child. Inform
				// the new child's viewer's that it's URL has changed.
				//
				_receivers.notify( this, kChildren );
				//
				// The child model here shouldn't actually have any
				// receivers at this point, but we notify anyway.
				//
				m->_receivers.notify( m, kUrl );
				m->_receivers.notify( m, kParent );
			} catch( std::exception& ex ) {
				if( m ) m->release();
				return adobe::value_t( false );
			}
			if( m ) m->release();
			return adobe::value_t( true );
		} else if( action == kRotate ) {
			//
			// Via this hack, rotating JPEG images is supported.
			//
			if( getAttribute( kType ).get<std::string>() != "image/jpeg" )
				return adobe::value_t( false );
			//
			// Attempt to get the angle of rotation. It must be 90, 180 or 270.
			//
			unsigned int angle = 0;
			if( !arg.get<unsigned int>( angle ) ) return adobe::value_t( false );
			if( (angle != 90) && (angle != 180) && (angle != 270) ) return adobe::value_t( false );
			//
			// Determine the rotation code.
			//
			JXFORM_CODE xform( JXFORM_ROT_90 );
			if( angle == 180 ) xform = JXFORM_ROT_180;
			else if( angle == 270 ) xform = JXFORM_ROT_270;
			//
			// Perform the reorientation.
			//
			char* thumbnail( new char[THUMB_MAX] );
			char* filename( strdup( _path.native_file_string().c_str() ) );
			int flags( JFLAG_TRANSFORM_IMAGE | JFLAG_TRANSFORM_THUMBNAIL | JFLAG_UPDATE_ORIENTATION );
			int tsize = create_thumbnail( filename, (unsigned char*) thumbnail, THUMB_MAX );
			jpeg_transform_inplace( filename, xform, NULL, thumbnail, tsize, flags );
			delete[] thumbnail;
			free( filename );
			//
			// Request that our icon get regenerated. When we get told of the
			// change we will notify listeners, so we don't need to notify
			// here.
			//
			std::string type = 
				getAttribute( kType ).get<std::string>();
			_icon = _loadQueue->load( type, _path, this );
			_receivers.notify( this, kData );
			_receivers.notify( this, kSize );
			_receivers.notify( this, kStreamBuf );
			return adobe::value_t( true );
		}
		return adobe::value_t( false );
	}
	void fileModel::iconComplete( const adobe::value_t& icon ) {
		_scheduled = false;
		_icon = icon;
		_receivers.notify( this, kIcon );
	}
	std::string fileModel::_getParentUrl() const {
		std::string parent = "file://localhost/";
		//
		// The branch_path is the parent of whatever we are.
		//
		parent += _path.branch_path().string();
		return parent;
	}
	std::string fileModel::_buildUrl( const bfs::path& p ) {
		return "file://" + p.string();
	}
};

